package com.naja.auth2server.security;

import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsent;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @auther wangjianying
 * @date 2023/12/18 10:12
 */
public class RedisOAuth2AuthorizationConsentService implements OAuth2AuthorizationConsentService {
    @Autowired
    private RedissonClient redisson;

    private static final String AUTHORIZATION_CONSENT = "oauth2:token_consent";

    private final static Long TIMEOUT = 600L;

    @Override
    public void save(OAuth2AuthorizationConsent authorizationConsent) {
        Assert.notNull(authorizationConsent, "authorizationConsent cannot be null");
        Map<String,Object> parameters = new HashMap<>();
        parameters.put("registeredClientId", authorizationConsent.getRegisteredClientId());
        parameters.put("principalName", authorizationConsent.getPrincipalName());
        Set<String> authorities = new HashSet<>();
        for (GrantedAuthority authority : authorizationConsent.getAuthorities()) {
            authorities.add(authority.getAuthority());
        }
        parameters.put("authorities", StringUtils.collectionToDelimitedString(authorities, ","));

        RBucket<Map<String,Object>> bucket = redisson.getBucket(buildKey(authorizationConsent));
        bucket.set(parameters,TIMEOUT, TimeUnit.SECONDS);
    }

    @Override
    public void remove(OAuth2AuthorizationConsent authorizationConsent) {
        Assert.notNull(authorizationConsent, "authorizationConsent cannot be null");
        RBucket<Map<String,Object>> bucket = redisson.getBucket(buildKey(authorizationConsent));
        bucket.delete();
    }

    @Override
    public OAuth2AuthorizationConsent findById(String registeredClientId, String principalName) {
        Assert.hasText(registeredClientId, "registeredClientId cannot be empty");
        Assert.hasText(principalName, "principalName cannot be empty");
        RBucket<Map<String,Object>> bucket = redisson.getBucket(buildKey(registeredClientId, principalName));
        Map<String, Object> map = bucket.get();
        if (map == null) {
            return null;
        }
        OAuth2AuthorizationConsent.Builder builder = OAuth2AuthorizationConsent.withId(registeredClientId, principalName);
        String authorizationConsentAuthorities = (String) map.get("authorities");
        if (authorizationConsentAuthorities != null) {
            for (String authority : StringUtils.commaDelimitedListToSet(authorizationConsentAuthorities)) {
                builder.authority(new SimpleGrantedAuthority(authority));
            }
        }
        return builder.build();
    }

    private static String buildKey(String registeredClientId, String principalName) {
        return String.format("%s:%s:%s", AUTHORIZATION_CONSENT, registeredClientId, principalName);
    }

    private static String buildKey(OAuth2AuthorizationConsent authorizationConsent) {
        return buildKey(authorizationConsent.getRegisteredClientId(), authorizationConsent.getPrincipalName());
    }

}
